/**************************************************************************************************
  Filename:       pulseGATTprofile.c
  Revised:        $Date: 2011-08-06 08:56:11 -0700 (Fri, 06 Aug 2010) $
  Revision:       $Revision: 23333 $

  Description:    This file contains the pulse GATT profile sample GATT service 
                  profile for use with the BLE sample application.

  Copyright 2011 Bifrost Logics. All rights reserved.

**************************************************************************************************/

/*********************************************************************
 * INCLUDES
 */
#include "bcomdef.h"
#include "OSAL.h"
#include "linkdb.h"
#include "att.h"
#include "gatt.h"
#include "gatt_uuid.h"
#include "gattservapp.h"

#include "pulseGATTprofile.h"

/*********************************************************************
 * MACROS
 */

/*********************************************************************
 * CONSTANTS
 */

#define SERVAPP_NUM_ATTR_SUPPORTED        14

/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */
// Pulse GATT Profile Service UUID: 0xFFF0
CONST uint8 pulseProfileServUUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(PULSEPROFILE_SERV_UUID), HI_UINT16(PULSEPROFILE_SERV_UUID)
};

// Frequency UUID: 0xFFF1
CONST uint8 pulseProfilefreqUUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(PULSEPROFILE_FREQ_UUID), HI_UINT16(PULSEPROFILE_FREQ_UUID)
};

// ECG UUID: 0xFFF2
CONST uint8 pulseProfileECGUUID[ATT_BT_UUID_SIZE] =
{ 
  LO_UINT16(PULSEPROFILE_ECG_UUID), HI_UINT16(PULSEPROFILE_ECG_UUID)
};


/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */

static uint16 pulseProfileConnHandle = INVALID_CONNHANDLE;

/*********************************************************************
 * Profile Attributes - variables
 */

// Pulse Profile Service attribute
static CONST gattAttrType_t pulseProfileService = { ATT_BT_UUID_SIZE, pulseProfileServUUID };


// Pulse Profile Freq Properties
static uint8 pulseProfileFreqProps = GATT_PROP_READ;

// Pulse Profile Freq Value
static uint8 pulseProfileFreq = 0;

// Pulse Profile Freq User Description
static uint8 pulseProfileFreqUserDesp[16] = "Pulse frequency\0";


// Pulse Profile ECG Properties
static uint8 pulseProfileECGProps = GATT_PROP_READ;

// Pulse Profile ECG Value
static uint8 pulseProfileECG = 0;

// Pulse Profile ECG User Description
static uint8 pulseProfileECGUserDesp[11] = "ECG values\0";

/*********************************************************************
 * Profile Attributes - Table
 */

static gattAttribute_t pulseProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] = 
{
  // Pulse Profile Service
  { 
    { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */
    GATT_PERMIT_READ,                         /* permissions */
    0,                                        /* handle */
    (uint8 *)&pulseProfileService            /* pValue */
  },

  // Frequency Declaration
  { 
    { ATT_BT_UUID_SIZE, characterUUID },
    GATT_PERMIT_READ, 
    0,
    &pulseProfileFreqProps 
  },

  // Frequency Value 
  { 
    { ATT_BT_UUID_SIZE, pulseProfilefreqUUID },
    GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
    0, 
    &pulseProfileFreq 
  },

  // Frequency User Description
  { 
    { ATT_BT_UUID_SIZE, charUserDescUUID },
    GATT_PERMIT_READ, 
    0, 
    pulseProfileFreqUserDesp 
  },      
        

};


/*********************************************************************
 * LOCAL FUNCTIONS
 */
static uint8 pulseProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, 
                            uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen );
static bStatus_t pulseProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                 uint8 *pValue, uint8 len, uint16 offset );

/*********************************************************************
 * NETWORK LAYER CALLBACKS
 */

/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      PulseProfile_AddService
 *
 * @brief   Initializes the Pulse Profile service by registering
 *          GATT attributes with the GATT server.
 *
 * @param   services - services to add. This is a bit map and can
 *                     contain more than one service.
 *
 * @return  Success or Failure
 */
bStatus_t PulseProfile_AddService( uint32 services )
{
  uint8 status = SUCCESS;

  if ( services & PULSEPROFILE_SERVICE )
  {
    // Register GATT attribute list and CBs with GATT Server App
    status = GATTServApp_RegisterService( pulseProfileAttrTbl, GATT_NUM_ATTRS( pulseProfileAttrTbl ),
                                          pulseProfile_ReadAttrCB, pulseProfile_WriteAttrCB, NULL );
  }

  return ( status );
}

/*********************************************************************
 * @fn      PulseProfile_SetParameter
 *
 * @brief   Set a Pulse Profile parameter.
 *
 * @param   param - Profile parameter ID
 * @param   len - length of data to right
 * @param   value - pointer to data to write.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate 
 *          data type (example: data type of uint16 will be cast to 
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t PulseProfile_SetParameter( uint8 param, uint8 len, void *value )
{
  bStatus_t ret = SUCCESS;
  switch ( param )
  {
    case PULSEPROFILE_FREQ:
      if ( len == sizeof ( uint8 ) ) 
      {
        pulseProfileFreq = *((uint8*)value);
      }
      else
      {
        ret = bleInvalidRange;
      }
      break;
      
    default:
      ret = INVALIDPARAMETER;
      break;
  }
  
  return ( ret );
}

/*********************************************************************
 * @fn      PulseProfile_GetParameter
 *
 * @brief   Get a Pulse Profile parameter.
 *
 * @param   param - Profile parameter ID
 * @param   value - pointer to data to put.  This is dependent on
 *          the parameter ID and WILL be cast to the appropriate 
 *          data type (example: data type of uint16 will be cast to 
 *          uint16 pointer).
 *
 * @return  bStatus_t
 */
bStatus_t PulseProfile_GetParameter( uint8 param, void *value )
{
  bStatus_t ret = SUCCESS;
  switch ( param )
  {
    case PULSEPROFILE_FREQ:
      *((uint8*)value) = pulseProfileFreq;
      break;

    default:
      ret = INVALIDPARAMETER;
      break;
  }
  
  return ( ret );
}

/*********************************************************************
 * @fn          pulseProfile_ReadAttrCB
 *
 * @brief       Read an attribute.
 *
 * @param       connHandle - connection message was received on
 * @param       pAttr - pointer to attribute
 * @param       pValue - pointer to data to be read
 * @param       pLen - length of data to be read
 * @param       offset - offset of the first octet to be read
 * @param       maxLen - maximum length of data to be read
 *
 * @return      Success or Failure
 */
static uint8 pulseProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr, 
                            uint8 *pValue, uint8 *pLen, uint16 offset, uint8 maxLen )
{
  bStatus_t status = SUCCESS;

  // If attribute permissions require authorization to read, return error
  if ( gattPermitAuthorRead( pAttr->permissions ) )
  {
    // Insufficient authorization
    return ( ATT_ERR_INSUFFICIENT_AUTHOR );
  }
  
  // Make sure it's not a blob operation (no attributes in the profile are long)
  if ( offset > 0 )
  {
    return ( ATT_ERR_ATTR_NOT_LONG );
  }
 
  if ( pAttr->type.len == ATT_BT_UUID_SIZE )
  {
    // 16-bit UUID
    uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
    switch ( uuid )
    {
      // No need for "GATT_SERVICE_UUID" or "GATT_CLIENT_CHAR_CFG_UUID" cases;
      // gattserverapp handles those types for reads

      case PULSEPROFILE_FREQ_UUID:
        *pLen = 1;
        pValue[0] = *pAttr->pValue;
        break;
        
      default:
        // Should never get here! (characteristics 3 and 4 do not have read permissions)
        *pLen = 0;
        status = ATT_ERR_ATTR_NOT_FOUND;
        break;
    }
  }
  else
  {
    // 128-bit UUID
    *pLen = 0;
    status = ATT_ERR_INVALID_HANDLE;
  }

  return ( status );
}

/*********************************************************************
 * @fn      pulseProfile_WriteAttrCB
 *
 * @brief   Validate attribute data prior to a write operation
 *
 * @param   connHandle - connection message was received on
 * @param   pAttr - pointer to attribute
 * @param   pValue - pointer to data to be written
 * @param   len - length of data
 * @param   offset - offset of the first octet to be written
 * @param   complete - whether this is the last packet
 * @param   oper - whether to validate and/or write attribute value  
 *
 * @return  Success or Failure
 */
static bStatus_t pulseProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,
                                 uint8 *pValue, uint8 len, uint16 offset )
{
  bStatus_t status = SUCCESS;

  // If attribute permissions require authorization to write, return error
  if ( gattPermitAuthorWrite( pAttr->permissions ) )
  {
    // Insufficient authorization
    return ( ATT_ERR_INSUFFICIENT_AUTHOR );
  }
  
  if ( pAttr->type.len == ATT_BT_UUID_SIZE )
  {
    // 16-bit UUID
    uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
    switch ( uuid )
    {
      case PULSEPROFILE_FREQ_UUID:

        //Validate the value
        // Make sure it's not a blob oper
        if ( offset == 0 )
        {
          if ( len > 1 )
            status = ATT_ERR_INVALID_VALUE_SIZE;
        }
        else
        {
          status = ATT_ERR_ATTR_NOT_LONG;
        }
        
        //Write the value
        if ( status == SUCCESS )
        {
          uint8 *pCurValue = (uint8 *)pAttr->pValue;        
          *pCurValue = pValue[0];   
        }
             
        break;

      case GATT_CLIENT_CHAR_CFG_UUID:
        // Validate the value
        // Make sure it's not a blob operation
        if ( offset == 0 )
        {
          if ( len == 2 )
          {
            uint16 charCfg = BUILD_UINT16( pValue[0], pValue[1] );
            
            // Validate characteristic configuration bit field
            if ( !( charCfg == GATT_CLIENT_CFG_NOTIFY   ||
                    charCfg == GATT_CFG_NO_OPERATION ) )
            {
              status = ATT_ERR_INVALID_VALUE;
            }
          }
          else
          {
            status = ATT_ERR_INVALID_VALUE_SIZE;
          }
        }
        else
        {
          status = ATT_ERR_ATTR_NOT_LONG;
        }
  
        // Write the value
        if ( status == SUCCESS )
        {
          uint16 *pCurValue = (uint16 *)pAttr->pValue;
          
          *pCurValue = BUILD_UINT16( pValue[0], pValue[1] );
        
          pulseProfileConnHandle = connHandle;
        }
        break;
        
      default:
        // Should never get here! (characteristics 2 and 4 do not have write permissions)
        status = ATT_ERR_ATTR_NOT_FOUND;
        break;
    }
  }
  else
  {
    // 128-bit UUID
    status = ATT_ERR_INVALID_HANDLE;
  }

  return ( status );
}

/*********************************************************************
*********************************************************************/
